package ro.gillardon.commons.util;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.function.Function;
/**
 * Conversions between LocalDateTime and Date.
 */
public final class DateUtils {

  /**
   * Instantiates a new DateUtils.
   */
  private DateUtils() {
    // constructor without parameters
  }

  /**
   * Converts from Date to LocalDateTime.
   *
   * @param date
   * @return LocalDateTime
   */
  public static LocalDateTime convertToLocalDateTime(final Date date) {
    // method reference definition
    Function<Date, LocalDateTime> localDateConverterMethod =
      (localDateTime) -> localDateTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    // pass the method reference
    return checkIfNullAndConvert(localDateConverterMethod, date);

  }

  /**
   * Converts from Date to LocalDate.
   *
   * @param date
   * @return LocalDate
   */
  public static LocalDate convertToLocalDate(final Date date) {
    // method reference definition
    Function<Date, LocalDate> localDateConverterMethod =
      (localDate) -> localDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    // pass the method reference
    return checkIfNullAndConvert(localDateConverterMethod, date);
  }

  /**
   * Converts from LocalDateTime to Date.
   *
   * @param localDateTime
   * @return Date
   */
  public static Date convertToDate(final LocalDateTime localDateTime) {
    // method reference definition
    Function<LocalDateTime, Date> localDateConverterMethod =
      (date) -> Date.from(date.atZone(ZoneId.systemDefault()).toInstant());
    // pass the method reference
    return checkIfNullAndConvert(localDateConverterMethod, localDateTime);
  }

  /**
   * Converts from LocalDate to Date.
   *
   * @param localDate
   * @return Date
   */
  public static Date convertToDate(final LocalDate localDate) {
    // method reference definition
    Function<LocalDate, Date> localDateConverterMethod =
      (date) -> Date.from(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    // pass the method reference
    return checkIfNullAndConvert(localDateConverterMethod, localDate);
  }

  /**
   * Creates a new instance (clone) of the original date to not expose the internal structure
   * 
   * @param originalDate to be cloned
   * @return the clone
   */
  public static Date createNewDate(final Date originalDate) {
    // method reference definition
    Function<Date, Date> dateCloner = (date) -> new Date(date.getTime());
    // pass the method reference
    return checkIfNullAndConvert(dateCloner, originalDate);
  }

  /**
   * Checks if param is null, if not converts it to the desired type, else returns null.
   *
   * @param converter converter function method reference
   * @param param the input param
   * @return the desired converted value
   */
  private static <T, R> R checkIfNullAndConvert(final Function<T, R> converter, final T param) {
    if (param == null) {
      return null;
    } else {
      return converter.apply(param);
    }

  }
}
